package com.wmeimob.fastboot.jiubei.service;

import cn.hutool.core.lang.Snowflake;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.response.AlipayTradeWapPayResponse;
import com.wmeimob.fastboot.alipay.service.AlipayService;
import com.wmeimob.fastboot.core.exception.CustomException;
import com.wmeimob.fastboot.jiubei.constant.RedissonKey;
import com.wmeimob.fastboot.jiubei.entity.Member;
import com.wmeimob.fastboot.jiubei.entity.ShopGoods;
import com.wmeimob.fastboot.jiubei.entity.ShopOrderItems;
import com.wmeimob.fastboot.jiubei.entity.ShopOrders;
import com.wmeimob.fastboot.jiubei.enums.PayTypesEnum;
import com.wmeimob.fastboot.jiubei.enums.ShopOrdersStatusEnum;
import com.wmeimob.fastboot.jiubei.mapper.MemberMapper;
import com.wmeimob.fastboot.jiubei.mapper.ShopGoodsMapper;
import com.wmeimob.fastboot.jiubei.mapper.ShopOrderItemsMapper;
import com.wmeimob.fastboot.jiubei.mapper.ShopOrdersMapper;
import com.wmeimob.fastboot.util.InputValidator;
import com.wmeimob.fastboot.util.RandomCodeUtil;
import com.wmeimob.fastboot.util.StringUtils;
import com.wmeimob.fastboot.wechat.starter.config.WechatPayProperties;
import com.wmeimob.fastboot.wechat.starter.dto.WechatJsPayDTO;
import com.wmeimob.fastboot.wechat.starter.service.WepayService;
import com.wmeimob.fastboot.jiubei.check.ShopOrderCheck;
import com.wmeimob.fastboot.jiubei.dto.GoodsDTO;
import com.wmeimob.fastboot.jiubei.dto.GoodsStockChangeResponseDTO;
import com.wmeimob.fastboot.wechat.starter.util.IpAddressHelper;
import lombok.extern.slf4j.Slf4j;
import me.hao0.wechat.core.Wechat;
import me.hao0.wepay.core.Wepay;
import me.hao0.wepay.model.pay.JsPayRequest;
import me.hao0.wepay.model.pay.JsPayResponse;
import me.hao0.wepay.model.pay.QrPayRequest;
import me.hao0.wepay.model.pay.QrPayResponse;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import tk.mybatis.mapper.entity.Example;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @version 1.0
 * @ClassName ShopOrdersServiceImpl
 * @Description 订单 服务类实现
 * @Author wjt
 * @Date Mon Aug 31 23:57:11 CST 2020
 **/

@Service
@Transactional(rollbackFor = {Exception.class})
@Slf4j
public class ShopOrdersServiceImpl implements ShopOrdersService {

    @Resource
    private ShopOrdersMapper shopOrdersMapper;
    @Resource
    private ShopOrderItemsMapper shopOrderItemsMapper;
    @Resource
    private ShopGoodsService shopGoodsService;
    @Resource
    private MemberMapper memberMapper;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private WepayService wepayService;
    @Resource
    private ShopOrderCheck shopOrderCheck;
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private ShopGoodsMapper shopGoodsMapper;
    @Resource
    private WechatPayProperties wepayProperties;
    @Resource
    private AlipayService alipayService;
    private static final String USER_SUBMIT_ORDER_KEY = "submit_order:user:%s";

    @Override
    public List<ShopOrders> list(ShopOrders shopOrders) {
        Example example = new Example(ShopOrders.class);
        Example.Criteria criteria = example.createCriteria();
        if (!StringUtils.isEmpty(shopOrders.getId())) {
            criteria.andEqualTo("id", shopOrders.getId());
        }
        if (!StringUtils.isEmpty(shopOrders.getOrderNo())) {
            criteria.andLike("orderNo", StringUtils.fullFuzzy(shopOrders.getOrderNo()));
        }
        if (!StringUtils.isEmpty(shopOrders.getUserId())) {
            criteria.andEqualTo("userId", shopOrders.getUserId());
        }
        if (!StringUtils.isEmpty(shopOrders.getUserName())) {
            criteria.andLike("userName", StringUtils.fullFuzzy(shopOrders.getUserName()));
        }
        if (!StringUtils.isEmpty(shopOrders.getUserCode())) {
            criteria.andLike("userCode", StringUtils.fullFuzzy(shopOrders.getUserCode()));
        }
        if (!StringUtils.isEmpty(shopOrders.getOrderType())) {
            criteria.andLike("orderType", StringUtils.fullFuzzy(shopOrders.getOrderType()));
        }
        if (!StringUtils.isEmpty(shopOrders.getMobile())) {
            criteria.andLike("mobile", StringUtils.fullFuzzy(shopOrders.getMobile()));
        }
        if (!StringUtils.isEmpty(shopOrders.getOrderStatus())) {
            criteria.andLike("orderStatus", StringUtils.fullFuzzy(shopOrders.getOrderStatus()));
        }
        if (!StringUtils.isEmpty(shopOrders.getShippingName())) {
            criteria.andLike("shippingName", StringUtils.fullFuzzy(shopOrders.getShippingName()));
        }
        if (!StringUtils.isEmpty(shopOrders.getShippingProvince())) {
            criteria.andLike("shippingProvince", StringUtils.fullFuzzy(shopOrders.getShippingProvince()));
        }
        if (!StringUtils.isEmpty(shopOrders.getShippingCity())) {
            criteria.andLike("shippingCity", StringUtils.fullFuzzy(shopOrders.getShippingCity()));
        }
        if (!StringUtils.isEmpty(shopOrders.getShippingDistrict())) {
            criteria.andLike("shippingDistrict", StringUtils.fullFuzzy(shopOrders.getShippingDistrict()));
        }
        if (!StringUtils.isEmpty(shopOrders.getShippingAddress())) {
            criteria.andLike("shippingAddress", StringUtils.fullFuzzy(shopOrders.getShippingAddress()));
        }
        if (!StringUtils.isEmpty(shopOrders.getShippingMobile())) {
            criteria.andLike("shippingMobile", StringUtils.fullFuzzy(shopOrders.getShippingMobile()));
        }
        if (!StringUtils.isEmpty(shopOrders.getShippingFee())) {
            criteria.andEqualTo("shippingFee", shopOrders.getShippingFee());
        }
        if (!StringUtils.isEmpty(shopOrders.getTransactionId())) {
            criteria.andLike("transactionId", StringUtils.fullFuzzy(shopOrders.getTransactionId()));
        }
        if (!StringUtils.isEmpty(shopOrders.getOrderAmount())) {
            criteria.andEqualTo("orderAmount", shopOrders.getOrderAmount());
        }
        if (!StringUtils.isEmpty(shopOrders.getPayAmount())) {
            criteria.andEqualTo("payAmount", shopOrders.getPayAmount());
        }
        if (!StringUtils.isEmpty(shopOrders.getShippingDeduction())) {
            criteria.andEqualTo("shippingDeduction", shopOrders.getShippingDeduction());
        }
        if (!StringUtils.isEmpty(shopOrders.getPayAt())) {
            criteria.andEqualTo("payAt", shopOrders.getPayAt());
        }
        if (!StringUtils.isEmpty(shopOrders.getPayType())) {
            criteria.andEqualTo("payType", shopOrders.getPayType());
        }
        if (!StringUtils.isEmpty(shopOrders.getRemark())) {
            criteria.andLike("remark", StringUtils.fullFuzzy(shopOrders.getRemark()));
        }
        if (!StringUtils.isEmpty(shopOrders.getUserComments())) {
            criteria.andLike("userComments", StringUtils.fullFuzzy(shopOrders.getUserComments()));
        }
        if (!StringUtils.isEmpty(shopOrders.getShippingVendor())) {
            criteria.andLike("shippingVendor", StringUtils.fullFuzzy(shopOrders.getShippingVendor()));
        }
        if (!StringUtils.isEmpty(shopOrders.getShippingNo())) {
            criteria.andLike("shippingNo", StringUtils.fullFuzzy(shopOrders.getShippingNo()));
        }
        if (!StringUtils.isEmpty(shopOrders.getShippingAt())) {
            criteria.andEqualTo("shippingAt", shopOrders.getShippingAt());
        }
        if (!StringUtils.isEmpty(shopOrders.getReceiptAt())) {
            criteria.andEqualTo("receiptAt", shopOrders.getReceiptAt());
        }
        if (!StringUtils.isEmpty(shopOrders.getCommentAt())) {
            criteria.andEqualTo("commentAt", shopOrders.getCommentAt());
        }
        if (!StringUtils.isEmpty(shopOrders.getClosedAt())) {
            criteria.andEqualTo("closedAt", shopOrders.getClosedAt());
        }
        if (!StringUtils.isEmpty(shopOrders.getClosedReason())) {
            criteria.andLike("closedReason", StringUtils.fullFuzzy(shopOrders.getClosedReason()));
        }
        if (!StringUtils.isEmpty(shopOrders.getSettleCommission())) {
            criteria.andEqualTo("settleCommission", shopOrders.getSettleCommission());
        }
        if (!StringUtils.isEmpty(shopOrders.getComment())) {
            criteria.andEqualTo("comment", shopOrders.getComment());
        }
        if (!StringUtils.isEmpty(shopOrders.getGmtCreate())) {
            criteria.andEqualTo("gmtCreate", shopOrders.getGmtCreate());
        }
        if (!StringUtils.isEmpty(shopOrders.getGmtModified())) {
            criteria.andEqualTo("gmtModified", shopOrders.getGmtModified());
        }
        if (!StringUtils.isEmpty(shopOrders.getDel())) {
            criteria.andEqualTo("del", shopOrders.getDel());
        }
        if (!StringUtils.isEmpty(shopOrders.getRefund())) {
            criteria.andEqualTo("refund", shopOrders.getRefund());
        }
        criteria.andEqualTo("del", false);
        example.orderBy("id").desc();
        List<ShopOrders> shopOrderss = shopOrdersMapper.selectByExample(example);
        return shopOrderss;
    }

    @Override
    public ShopOrders queryShopOrdersById(Long id) {
        return shopOrdersMapper.selectByPrimaryKey(id);
    }


    @Override
    public void addShopOrders(ShopOrders shopOrders) {
        shopOrders.setGmtCreate(new Date());
        shopOrdersMapper.insertSelective(shopOrders);
    }

    @Override
    public void removeShopOrders(Long id) {
        ShopOrders shopOrders = new ShopOrders();
        shopOrders.setId(id);
        shopOrders.setDel(true);
        shopOrdersMapper.updateByPrimaryKeySelective(shopOrders);
    }

    @Override
    public void modifyShopOrders(ShopOrders shopOrders) {
        shopOrders.setGmtModified(new Date());
        shopOrdersMapper.updateByPrimaryKeySelective(shopOrders);
    }

    @Override
    public List<ShopOrders> findListByCondition(ShopOrders shopOrders) {
        return shopOrdersMapper.selectOrderList(shopOrders);
    }

    @Override
    public ShopOrders findShopOrdersById(Long id) {
        ShopOrders param = new ShopOrders();
        param.setId(id);
        ShopOrders orders = shopOrdersMapper.selectOrdersDetailById(param);
        Assert.notNull(orders, "订单不存在");
        return orders;
    }

    @Override
    public void ship(ShopOrders orders) {
        InputValidator.checkNull(orders.getId(), "订单id");

        ShopOrders queryResult = shopOrdersMapper.selectByPrimaryKey(orders.getId());
        if (queryResult == null) {
            throw new CustomException("订单不存在, 请确认后重试");
        }

        if (!queryResult.getOrderStatus().equals(ShopOrdersStatusEnum.DELIVERED.name())) {
            throw new CustomException("订单状态不正确");
        }

        shopOrdersMapper.updateByPrimaryKeySelective(ShopOrders.builder().id(orders.getId())
                .orderStatus(ShopOrdersStatusEnum.SEND.name()).shippingAt(new Date()).build());
    }

    @Override
    public ShopOrders calculateOrders(ShopOrders orders) {

        //检查商品,并获取商品信息
        List<ShopGoods> shopGoods = checkOrderGoods(orders);
        //计算商品价格
        orders = fromGoods(orders, shopGoods);
        orders.setUserId(orders.getUserId());
        //运费相关计算
        calShippingAmount(orders);
        return orders;
    }


    private void calShippingAmount(ShopOrders orders) {
        Member member = memberMapper.selectByPrimaryKey(orders.getUserId());
        if (ObjectUtils.isEmpty(member)) {
            throw new CustomException("数据异常");

        }
    }

    /**
     * 信息构造
     *
     * @param orders
     * @return
     */
    public ShopOrders fromGoods(ShopOrders orders, List<ShopGoods> shopGoods) {
        List<ShopOrderItems> orderItems = orders.getItems();
        Map<Integer, ShopGoods> goodsMap = shopGoods.stream().collect(Collectors.toMap(ShopGoods::getId, goods -> goods, (k1, k2) -> k1));
        if (StringUtils.isEmpty(orders.getOrderAmount())) {
            orders.setOrderAmount(BigDecimal.ZERO);
        }
        if (StringUtils.isEmpty(orders.getPayAmount())) {
            orders.setPayAmount(BigDecimal.ZERO);
        }
        for (ShopOrderItems items : orderItems) {
            ShopGoods goods = goodsMap.get(items.getGoodsId());
            items.setGoodsId(goods.getId());
            items.setGoodsName(goods.getName());
            items.setGoodsImg(goods.getGoodsImgs());
            items.setGoodsSkuName(goods.getSpecification());
            //销售价
            items.setSalePrice(goods.getPrice());
            items.setItemsAmount(items.getSalePrice().multiply(BigDecimal.valueOf(items.getSaleQuantity())));
            //单个商品实际收款
            items.setItemsPayAmount(items.getItemsAmount());
            log.info(" 商品付款金额 {}", items.getItemsPayAmount());
            //订单金额累加
            orders.setOrderAmount(orders.getOrderAmount().add(items.getItemsAmount()));
            //订单实际支付金额累加
            orders.setPayAmount(orders.getPayAmount().add(items.getItemsPayAmount()));
        }
        log.info("初始订单金额 {}", orders.getPayAmount());
        return orders;
    }

    private List<ShopGoods> checkOrderGoods(ShopOrders orders) {
        List<ShopOrderItems> items = orders.getItems();
        //验证商品属性
        if (ObjectUtils.isEmpty(items)) {
            throw new CustomException("未选择任何商品");
        }
        List<Integer> goodsIdList = items.stream().filter(item -> item.getGoodsId() != null).map(ShopOrderItems::getGoodsId).collect(Collectors.toList());
        ShopGoods goods = new ShopGoods();
        goods.setIdList(goodsIdList);
        List<ShopGoods> goodsInfos = shopGoodsService.findByIdList(goods);
        if (ObjectUtils.isEmpty(goodsInfos)) {
            throw new CustomException("商品信息不存在");
        }
        goodsInfos.forEach(good -> {
            ShopGoods extract = extractGoodsFromGoodsList(good, goodsInfos);
            if (extract == null) {
                throw new CustomException("商品不存在");
            }
            if (!extract.getGrounding()) {
                throw new CustomException("商品[" + extract.getName() + "]已下架");
            }
        });
        return goodsInfos;
    }

    /**
     * 查询商品是否在一个集合里
     *
     * @param qg
     * @param queryResultGoods
     * @return
     */
    public ShopGoods extractGoodsFromGoodsList(ShopGoods qg, List<ShopGoods> queryResultGoods) {
        for (ShopGoods goods : queryResultGoods) {
            if (goods.getId().equals(qg.getId())) {
                return goods;
            }
        }
        return null;
    }


    @Override
    public ShopOrders confirmOrder(ShopOrders orders) {
        String key = String.format(USER_SUBMIT_ORDER_KEY, orders.getUserId());
        Boolean ifAbsent = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 3, TimeUnit.SECONDS);
        if (null == ifAbsent || !ifAbsent) {
            log.info("用户ID {} 下单操作过于频繁", orders.getUserId());
            throw new CustomException("操作过于频繁，请稍后再试");
        }
        List<GoodsDTO> goodsDtoList = shopOrderCheck.convertOrderItemsToGoodsInfo(orders.getItems());
        //扣减库存
        List<GoodsStockChangeResponseDTO> stockResponse = shopGoodsService.inventoryReduction(goodsDtoList);
        if (!stockResponse.isEmpty()) {
            StringBuffer sb = new StringBuffer("商品: ");
            stockResponse.forEach(s -> sb.append(s.getGoodsName()).append(" "));
            sb.append("库存不足");
            throw new CustomException(sb.toString());
        }
        orders.setOrderNo(new Snowflake(1, 1).nextIdStr());
        orders.setGmtCreate(new Date());
        int i = shopOrdersMapper.insertSelective(orders);
        if (i > 0) {
            orders.getItems().forEach(localOrderItems -> {
                localOrderItems.setOrderId(orders.getId());
                localOrderItems.setOrderNo(orders.getOrderNo());
                shopOrderItemsMapper.insertSelective(localOrderItems);
            });
        } else {
            throw new CustomException("订单生成失败，请稍后再试");
        }
        return orders;
    }

    @Override
    public Map<String, Object> wechatPay(ShopOrders orders) {

        Member member = memberMapper.selectByPrimaryKey(orders.getUserId());
        //构建支付参数
        WechatJsPayDTO wechatJsPayDTO = new WechatJsPayDTO();
        wechatJsPayDTO.setBody("订单支付");
        wechatJsPayDTO.setOrderNo(orders.getOrderNo());
        wechatJsPayDTO.setTotalFee(orders.getPayAmount());
        //todo 测试
        //------------------------------------
        wechatJsPayDTO.setTotalFee(new BigDecimal(0.01));
        wechatJsPayDTO.setNotifyUrl("https://wjt-jiubei.f.wmeimob.com/wechat/orders/notify");
        wechatJsPayDTO.setIsTest(true);
        //------------------------------------
        //--------正式环境---------
        //wechatJsPayDTO.setTotalFee(order.getPayAmount());
        //wechatJsPayDTO.setIsTest(false);
        //wechatJsPayDTO.setNotifyUrl("https://wjt-jiubei.f.wmeimob.com/wechat/orders/notify");
        //wechatJsPayDTO.setOpenid(member.getOpenid());
        //wechatJsPayDTO.setAppid(member.getAppid());
        JsPayResponse jsPayResponse = wepayService.prePay(wechatJsPayDTO);
        Map<String, Object> data = new HashMap<>(2);
        data.put("pay", jsPayResponse);
        data.put("id", orders.getId());
        return data;
    }

    @Override
    public Map<String, Object> aliPay(ShopOrders orders) {
        String subject = "支付宝订单支付";
        String body = "支付宝订单支付";
        AlipayTradeWapPayResponse alipayTradeWapPayResponse = null;
        try {
            alipayTradeWapPayResponse = alipayService.wapAlipay(orders.getOrderNo(), subject, body, orders.getPayAmount().toString());
            log.info("alipayTradeWapPayResponse==>" + JSONObject.toJSONString(alipayTradeWapPayResponse));
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
        Map<String, Object> data = new HashMap<>(2);
        data.put("pay", alipayTradeWapPayResponse);
        data.put("id", orders.getId());
        return data;
    }

    @Override
    public Map<String, Object> pay(ShopOrders shopOrders) {
        Member member = memberMapper.selectByPrimaryKey(shopOrders.getUserId());
        // 动态获取微信支付对象
        Wepay wepay = wepayService.getApiComponent("appid");
        Map<String, Object> data = new HashMap<>(2);
        //判断是否是他人代付
        String orderTradeNo = new Snowflake(1, 1).nextIdStr();

        JsPayRequest jsPayRequest = new JsPayRequest();
        jsPayRequest.setBody("订单支付");
        jsPayRequest.setOutTradeNo(orderTradeNo);
        //jsPayRequest.setTotalFee(orders.getPayAmount().multiply(new BigDecimal("100")).intValue());
        jsPayRequest.setTotalFee(new BigDecimal("0.01").multiply(new BigDecimal("100")).intValue());
        jsPayRequest.setOpenId("openId");
        jsPayRequest.setClientIp(IpAddressHelper.getRemoteHost(((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest()));
        jsPayRequest.setNotifyUrl(wepayProperties.getPayNotifyUrl());
        jsPayRequest.setTimeStart(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")));

        log.info("预下单打印appid={},mchid={}", wepay.getAppId(), wepay.getMchId());
        log.info("微信下单请求参数：{}", JSON.toJSONString(jsPayRequest));
        JsPayResponse jsPayResponse = wepay.pay().jsPay(jsPayRequest);
        log.info("微信下单响应数据：{}", JSON.toJSONString(jsPayResponse));
        data.put("pay", jsPayResponse);

        ShopOrders shopOrders1 = new ShopOrders();
        shopOrders1.setId(shopOrders.getId());
        shopOrders1.setOrderTradeNo(orderTradeNo);
        shopOrdersMapper.updateByPrimaryKeySelective(shopOrders1);

        data.put("id", shopOrders1.getId());
        return data;
    }

    @Override
    public ShopOrders queryShopOrdersByNo(String orderNo) {
        return shopOrdersMapper.queryShopOrdersByNo(orderNo);
    }

    @Override
    public void updateForPaySuccess(ShopOrders orders) {
        List<GoodsDTO> goodsDtos = orders.getItems().stream().map(item -> GoodsDTO.builder().goodsId(item.getGoodsId()).actualSales(item.getSaleQuantity()).build()).collect(Collectors.toList());
        shopGoodsService.incrementSales(goodsDtos);
    }

    @Override
    public void rebackStock(ShopOrders shopOrders) {
        shopOrders.getItems().forEach(item -> {
            RLock lock = redissonClient.getLock(RedissonKey.STOCK + item.getGoodsId());
            log.info("[领取积分] 方法:rebackStock()获取到分布式锁" + lock.getName());
            boolean getLock = false;
            try {
                if (getLock = lock.tryLock(0L, 3L, TimeUnit.SECONDS)) {
                    if (!Thread.currentThread().isInterrupted()) {
                        //更新库存
                        ShopGoods shopGoods = new ShopGoods();
                        shopGoods.setId(item.getGoodsId());
                        shopGoods.setSaleNum(item.getSaleQuantity());
                        int i = shopGoodsMapper.incrementStock(shopGoods);
                        if (i <= 0) {
                            throw new CustomException("订单取消失败，请稍后再试");
                        }
                    } else {
                        getLock = false;
                    }
                } else {
                    log.info("Redisson分布式锁没有获得锁:{},ThreadName:{}", RedissonKey.STOCK, Thread.currentThread().getName());
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                unlock(lock, getLock);
            }
        });
    }


    private void unlock(RLock lock, boolean getLock) {
        if (!getLock) {
            log.info("Redisson分布式锁没有获得锁:{},ThreadName:{}", RedissonKey.STOCK, Thread.currentThread().getName());
        } else {
            //解锁
            lock.unlock();
            log.info("Redisson分布式锁释放锁:{},ThreadName :{}", RedissonKey.STOCK, Thread.currentThread().getName());
        }
    }

}
